/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

/**
 * 1.Promise是一个类，新建时传递一个立即执行的执行器
 * 2.promise中有三种状态，等待（pending），成功（fulfilled），失败（rejected）
 *      pending --> fulfilled;
 *      pending --> rejected;
 *      状态只能由等待变为成功或者等待变为失败，且一旦状态确定就不可更改
 * 3.resolve和reject方法是用来更改状态的，resolve -> fulfilled，reject -> rejected
 * 4.then方法的作用就是判断状态，如果成功就调用resolve，失败就调用reject，then方法是定义在原型对象中的
 * 5.then的回调函数带有参数，成功时表示成功的值，失败时表示失败的原因
 * 6.如果promise内执行的是异步代码时（异步后resolve或reject），如果直接调用then中的回调，导致无法获得执行结果，所以在then时先存储回调，在真正完成（resolve或reject）的时候再调用相应的回调
 * 7.如果promise的then多次调用应该添加多个处理函数的回调，此时需要将上一步存储的处理函数改造成集合
 * 8.then方法的链式调用，如果要链式调用，then方法应该返回一个promise对象
 * 9.错误捕获，执行器函数和then回调中
 * 10.将then方法中的参数改为可选项
 * 11.实现promise.all方法（全部成功才resolve，一个失败就reject）
 * 12.实现promise.resolve，如果接收的参数是promise了直接返回，不是则new一个然后resolve对应的参数
 * 13.finally方法的实现
 * 14.catch方法实现
 * 15.race方法实现，不管成功还是失败，先结束的先resolve或reject
 */

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }

    // 当前promise实例的状态
    status = PENDING
    // 成功时的值
    value = undefined
    // 失败时的原因
    error = undefined
    // 成功回调
    successCallback = []
    // 失败回调
    failCallback = []

    resolve = value => {
        // 如果状态不是等待，阻止状态改变
        if (this.status !== PENDING) return
        // 将状态改为成功
        this.status = FULFILLED
        // 保存成功的值
        this.value = value
        // 判断成功回调是否存在，如果存在就调用
        // this.successCallback.length > 0 && this.successCallback(this.value)
        while (this.successCallback.length) this.successCallback.shift()()
    }

    reject = error => {
        // 如果状态不是等待，阻止状态改变
        if (this.status !== PENDING) return
        // 将状态改为失败
        this.status = REJECTED
        // 保存失败的原因
        this.error = error
        // 判断失败回调是否存在，存在就调用
        // this.failCallback && this.failCallback(this.error)
        while (this.failCallback.length) this.failCallback.shift()()
    }

    then(successCallback, failCallback) {
        // then方法中两个传参设置为可选，如果不传，则相应往后
        // 如果是非function要改为function,否则promise.then(1).then(2)...这种无法往后传递promise(成功函数处理 忽略函数之外的其他值)
        successCallback = typeof successCallback === 'function' ? successCallback : value => value
        failCallback = typeof failCallback === 'function' ? failCallback : error => {
            throw error
        }
        // 为了实现链式调用，此时then需要返回一个promise对象
        let promise = new MyPromise((resolve, reject) => {
            // 根据状态调用对应的回调函数
            if (this.status === FULFILLED) {
                // 使代码变为异步，才能取到当前resolvePromise的参数promise对象
                setTimeout(() => {
                    try {
                        let callRes = successCallback(this.value)
                        resolvePromise(promise, callRes, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                // 使代码变为异步，才能取到当前resolvePromise的参数promise对象
                setTimeout(() => {
                    try {
                        let callRes = failCallback(this.error)
                        resolvePromise(promise, callRes, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else {
                // 等待
                // 将成功回调和失败回调先存储备份，当状态变为非pending时再调用
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let callRes = successCallback(this.value)
                            resolvePromise(promise, callRes, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let callRes = failCallback(this.error)
                            resolvePromise(promise, callRes, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise
    }

    finally(callback) {
        // 返回promise对象保证finally后续能继续使用then链式调用
        return this.then(value => {
            return MyPromise.resolve(callback.then(() => value))
        }, error => {
            return MyPromise.resolve(callback.then(() => error))
        })
    }

    catch(failCallback) {
        return this.then(undefined, failCallback)
    }

    static all(array) {
        let res = []
        let index = 0;

        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                res[key] = value
                index++
                if (index === array.length) {
                    resolve(res)
                }
            }

            for (let i = 0; i < array.length; i++) {
                if (array[i] instanceof MyPromise) {
                    // MyPromise对象
                    array[i].then(value => addData(i, value), error => reject(error))
                } else {
                    // 普通值
                    addData(i, array[i])
                }
            }
        })
    }

    static race(array) {
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                if (array[i] instanceof MyPromise) {
                    array[i].then(resolve, reject)
                } else {
                    resolve(array[i])
                }
            }
        })
    }

    static resolve(value) {
        if (value instanceof MyPromise) return value
        return new MyPromise(resolve => resolve(value))
    }
}

/**
 * 链式调用每次得判断then中callback返回的值
 * 如果是非promise，直接调用resolve；
 * 如果是promise，需要判断promise的结果，再决定是resolve还是reject
 * @param promise
 * @param callRes
 * @param resolve
 * @param reject
 */
function resolvePromise(promise, callRes, resolve, reject) {
    // 如果then方法中的回调函数返回的promise对象是自己当前的promise，导致promise循环报错，需要reject处理，return使函数不在向下执行
    if (promise === callRes) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (callRes instanceof MyPromise) {
        // 是promise
        // callRes.then(value => resolve(value), error => reject(error))
        // 简化
        callRes.then(resolve, reject)
    } else {
        // 不是promise
        resolve(callRes)
    }
}

module.exports = MyPromise
